<?php
/**
 * Swolf framework main class.
 *
 * @copyright Copyright (c) 2015 Swolf Software.
 * @author liuxiao<seanliu707@gmail.com>
 */

class BaseSwolf
{
	/**
	 * Defines whether the application should be in debug mode or not. Defaults to false.
	 */
// 	public static $debug = FALSE;

	/**
	 * @var array class map used by the Swolf autoloading mechanism.
	 * @see load()
	 */
	public static $classes = array();
	
	/**
	 * @var array object definitions indexed by their types.
	 */
	protected static $definitions = array();
	
	/**
	 * Returns a string representing the current version of the Swolf framework.
	 * @return string the version of Swolf framework
	 */
	public static function getVersion()
	{
		return '1.0';
	}
	
	/**
	 * Class autoload loader,
	 * 	and this method will sure the same class be required once only.
	 *
	 * @param string $className the fully qualified class name
	 * @return mixed teturn the class name or null
	 * @throws Exception if the class file does not exist
	 */
	public static function load($className)
	{
		if ( ! isset(static::$classes[$className]) ) {
			static::$classes[$className] = true;
			$className = preg_replace('/[\.\/]/', DIRECTORY_SEPARATOR, $className);
			$classFile = SW_PATH.DIRECTORY_SEPARATOR.$className.'.class.php';
			if (file_exists($classFile)) {
				require $classFile;
			}
			
			$rt = explode(DIRECTORY_SEPARATOR, $className);
			$rt_len = count($rt);
			return $rt[$rt_len-1];
		} else {
			return;
		}
	
		if (SW_DEBUG && !file_exists($classFile) ) {
			throw new Exception("Unable to find the class: '$className'");
		}
	}
	
	/**
	 * Creates a new object using the given configuration.
	 *
	 * The method supports creating an object based on a class name, a configuration array or
	 * an anonymous function.
	 *
	 * Usage examples:
	 *
	 * // create an object using a class name
	 * $object = Swolf::createObject('db\Connection');
	 * 
	 * // create an object using a configuration array
	 * $object = Swolf::createObject(array(
	 *     'class' => 'db\Connection',
	 *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
	 *     'username' => 'root',
	 *     'password' => '',
	 *     'charset' => 'utf8',
	 * ));
	 * 
	 * // create an object with two constructor parameters
	 * $object = Swolf::createObject('MyClass', array($param1, $param2));
	 *
	 * @param array|string $params the constructor parameters
	 * @return object the created object
	 * @throws InvalidConfigException if the configuration is invalid.
	 */
	public static function createObject($type, $params = '')
	{
	    if (isset(static::$definitions[$type])) {
	        return static::$definitions[$type];
	    }
	    
		if (is_string($type)) {
		    //load the class file and get the class name
			$className = self::load($type);
			//create object and init
			static::$definitions[$type] = static::build($className, $params);
			
			return static::$definitions[$type];
			
		} elseif (is_array($type) && isset($type['class'])) {
			$class = $type['class'];
			$className = self::load($class);
			unset($type['class']);
			static::$definitions[$class] = static::build($className, $params, $type);
			
			return static::$definitions[$class];
		} elseif (is_callable($type, true)) {
			return call_user_func($type, $params);
		} elseif (SW_DEBUG && is_array($type)) {
			throw new Exception('Object configuration must be an array containing a "class" element.');
		} elseif (SW_DEBUG) {
			throw new Exception("Unsupported configuration type: " . gettype($type));
		}
	}
	
	/**
	 * Creates an instance of the specified class.
	 * @param string $class the class name
	 * @param array $params constructor parameters
	 * @param array $config configurations to be applied to the new instance
	 * @return object the newly created instance of the specified class
	 */
	protected static function build($class, $params, $config = array())
	{
		$_parameters = null;
		$_obj = null;
		
		if (!empty($params) && is_array($params)) {
			foreach ($params as $value) {
				$_parameters .= $value.',';
			}
			$_parameters = substr($_parameters, 0, -1);
		} elseif (is_string($params)) {
			$_parameters = $params;
		}
		
		$_obj = new $class($_parameters);
		
		if (!empty($config)) {
			foreach ($config as $name => $value) {
				$_obj->$name = $value;
			}
		}
		return $_obj;
	}
}